package com.hccl.service.gate;

import com.hccl.mongoEntities.GateAnalyseResultEntity;
import com.hccl.mongoEntities.GrammarVariableEntity;
import entities.SLUResult;
import gate.*;
import gate.corpora.DocumentContentImpl;
import gate.creole.ExecutionException;
import gate.creole.SerialAnalyserController;
import gate.creole.gazetteer.AbstractGazetteer;

import java.io.File;
import java.util.*;

import static com.hccl.config.Constants.gappUrl;

/**
 * 基于Gate的语义匹配类
 */
public class GateMatcher {

    private SerialAnalyserController analyser;
    private Document gateDoc;
    private DefaultFunction defaultFunction;


    /**
     * description:构造函数:初始化GateMacher和默认赋值函数DefaultFunction
     *
     * @return
     * @Param: 语法含有的变量List
     */
    public GateMatcher(List<GrammarVariableEntity> grammarVariables) throws Exception {
        this.analyser = (SerialAnalyserController) gate.util.persistence.PersistenceManager
                .loadObjectFromFile(new File(gappUrl));
        this.defaultFunction = new DefaultFunction(getAbstractGazetteer(), grammarVariables);
        Corpus corpus = Factory.newCorpus("BatchProcessApp Corpus");
        this.gateDoc = Factory.newDocument("");
        this.gateDoc.setParameterValue("encoding", "UTF-8");
        corpus.add(this.gateDoc);
        this.analyser.setCorpus(corpus);
    }


    /**
     * description: Gate匹配流程性函数
     *
     * @return Gate匹配结果实体
     * @Param: 用户输入识别文本input
     */
    public GateAnalyseResultEntity gateAnalyse(String input) throws Exception {

        AnnotationSet as = gateMatch(input);
        Set<Annotation> currentGaz = getGazetteer(gateDoc);
        Set<Annotation> setAnnotationFilterbyLengthandSlotNum = filterGateResult(as);
        //Set<Annotation> setAnnotationFilterbyLengthandSlotNum = as;
        ArrayList<SLUResult> gateResults = getSLUResultformGate(setAnnotationFilterbyLengthandSlotNum);
        String conflictReply = checkVariable(setAnnotationFilterbyLengthandSlotNum);
        ArrayList<SLUResult> finalResultsfromGate = combineGateResult(gateResults);
        return new GateAnalyseResultEntity(finalResultsfromGate, conflictReply, currentGaz);

    }

    /**
     * description: 执行Gate匹配，得到匹配结果
     *
     * @return 匹配结果AnnotationSet
     * @Param: 用户输入识别文本input
     */
    public AnnotationSet gateMatch(String input) throws ExecutionException {
        gateDoc.setContent(new DocumentContentImpl(input));
        analyser.execute();
        AnnotationSet as = gateDoc.getAnnotations("Candidates");
        return as;
    }

    private Set<Annotation> getGazetteer(Document doc) {
        Set<Annotation> currentGaz = new HashSet<>();
        AnnotationSet annotations = doc.getAnnotations("Gazetteer");
        for (Annotation as : annotations) {
            if (!as.getType().equals("Token")) {
                currentGaz.add(as);
            }
        }
        return currentGaz;
    }

    /**
     * description:根据有效长度EffectiveLength和语义槽个数SlotNum进行筛选
     *
     * @return 筛选后结果Set<Annotation>
     * @Param: Gate匹配结果AnnotationSet
     */
    private int iscontain(ArrayList<Long> arr1, ArrayList<Long> arr2) {
        int result = 0;
        Long arr1len = arr1.get(1)-arr1.get(0);
        Long arr2len = arr2.get(1)-arr2.get(0);
        if (arr1.get(0) >= arr2.get(0) && arr1.get(1) <= arr2.get(1)&& arr1len!=arr2len) {
            result = -1;
        }
        if (arr1.get(0) <= arr2.get(0) && arr1.get(1) >= arr2.get(1)&& arr1len!=arr2len) {
            result = 1;
        }
        return result;
    }

    private Set<Annotation> filterGateResult(AnnotationSet as) {
        //除##结点其他节点只保留最大的maxEffectiveLength
        //##节点去除位置重复或包含的slot
        double maxEffectiveLength = 0.0;
        Iterator it = as.iterator();
        String rule_node = "";
        for (Annotation a : as) {
            if (a.getFeatures().get("rule").equals("##")) {
                rule_node = "##";
                break;
            }
        }
        //String rule_node = (String)getFeatures().get("rule");
        if (rule_node.equals("##")) {
            Set<Annotation> setAnnotationFilter = new HashSet<>();
            ArrayList<ArrayList<Long>> indexlist = new ArrayList<ArrayList<Long>>();
            ArrayList<Annotation> Annotationlist = new ArrayList<Annotation>();
            for (Annotation a : as) {
                //##节点去除位置重复或包含的slot
                Long endindex = a.getEndNode().getOffset();
                Long startindex = a.getStartNode().getOffset();
                ArrayList<Long> ll = new ArrayList<>();
                ll.add(startindex);
                ll.add(endindex);
                indexlist.add(ll);
                Annotationlist.add(a);
            }
            Set<Annotation> markset = new HashSet<>();
            for (int i = 0; i < indexlist.size() - 1; i++) {
                for (int j = i + 1; j < indexlist.size(); j++) {
                    if (iscontain(indexlist.get(i), indexlist.get(j)) == 1) {
                        markset.add(Annotationlist.get(j));
                    }
                    if (iscontain(indexlist.get(i), indexlist.get(j)) == -1) {
                        markset.add(Annotationlist.get(i));
                    }
                }
            }
            for (Annotation a : as) {
                if (!markset.contains(a)) {
                    setAnnotationFilter.add(a);
                }
            }
            return setAnnotationFilter;

        } else {
            for (Annotation a : as) {
                rule_node = (String) a.getFeatures().get("rule");

                double curEffectiveLength = (double) a.getFeatures().get("effective_length");
                if (curEffectiveLength > maxEffectiveLength)
                    maxEffectiveLength = curEffectiveLength;
            }
            Set<Annotation> setAnnotationFilterbyLength = new HashSet<>();
            for (Annotation a : as) {
                double curEffectiveLength = (double) a.getFeatures().get("effective_length");
                if (Math.abs(curEffectiveLength - maxEffectiveLength) < 1e-20)
                    setAnnotationFilterbyLength.add(a);
            }
            int minSlotNum = Integer.MAX_VALUE;
            for (Annotation i : setAnnotationFilterbyLength) {
                int curSlotNum = (int) i.getFeatures().get("slotNum");
                if (curSlotNum < minSlotNum)
                    minSlotNum = curSlotNum;
            }
            //在maxEffectiveLength相等的情况下保留minSlotNum最少的
            Set<Annotation> setAnnotationFilterbySlotNum = new HashSet<>();
            for (Annotation i : setAnnotationFilterbyLength) {
                int curSlotNum = (int) i.getFeatures().get("slotNum");
                if (curSlotNum == minSlotNum)
                    setAnnotationFilterbySlotNum.add(i);
            }
            return setAnnotationFilterbySlotNum;
        }
    }

    /**
     * description: Gate若有多个结果合成冲突回复
     *
     * @return 冲突回复后的SLUResult
     * @Param: 筛选后结果setAnnotationFilterbySlotNum
     */
    public ArrayList<SLUResult> getSLUResultformGate(Set<Annotation> setAnnotationFilterbySlotNum) {

        ArrayList<SLUResult> res = new ArrayList<>();
        Set<SLUResult> matchSluResults = new HashSet<>();
        Set<SLUResult> noMatchSluResults = new HashSet<>();
        for (Annotation i : setAnnotationFilterbySlotNum) {
            String ruleName = (String) i.getFeatures().get("rule");
            String ruleType = (String) i.getFeatures().get("type");
            double curEffectiveLength = ((double) i.getFeatures().get("effective_length"));
            FeatureMap oriFeatures = (FeatureMap) i.getFeatures().get("slots");

            boolean slotMatch = true;
            Map<String, String> slots = new HashMap<>();


            if (defaultFunction != null) {//默认赋值函数的功能是？
                FeatureMap featureMapAdd = defaultFunction.assignRelatedVal(oriFeatures);
                for (Object key : featureMapAdd.keySet()) {
                    if (!oriFeatures.containsKey(key))
                        oriFeatures.put(key, featureMapAdd.get(key));

                    else if (!((FeatureMap) oriFeatures.get(key)).get("抽取名").
                            equals(((FeatureMap) featureMapAdd.get(key)).get("抽取名")))
                        slotMatch = false;
                }
            }

            if (oriFeatures.size() == 0 && ruleName.equals("##")) {
                //String MatchedToken = i.getFeatures()[0];
                String strtest = i.getFeatures().toString();
                String slotname = strtest.split(":")[2].split(",")[0];
                String slotmatch = strtest.split(":")[1].split("=")[0];
                slots.put(slotname, slotmatch);
                matchSluResults.add(new SLUResult(null, ruleName, ruleType, null, null, curEffectiveLength, slots));
                slotMatch = true;
                continue;
                //oriFeatures.put("slots",)
            }

            for (Object key : oriFeatures.keySet()) {
                String key1 = key.toString().replace("$", "");
                if (key1.equals("liuliang")) {
                    key1 = "流量数";
                }
                if (key1.equals("cost")) {
                    key1 = "金额";
                }
                if (key1.equals("month")) {
                    key1 = "月份";
                }

                if (key1.equals("package_type")) {
                    key1 = "套餐类型";
                }
                if (key1.equals("business_name")) {
                    key1 = "业务名称";
                }
                if (key1.equals("flow_name")) {
                    key1 = "流量包名";
                }
                if (key1.equals("group_business")) {
                    key1 = "集团业务名称";
                }
                if (key1.equals("discount")) {
                    key1 = "优惠类型";
                }
                slots.put(key1, (String) ((FeatureMap) oriFeatures.get(key)).get("抽取名"));
            }
            if (slotMatch)
                matchSluResults.add(new SLUResult(null, ruleName, ruleType, null, null, curEffectiveLength, slots));
            else
                noMatchSluResults.add(new SLUResult(null, ruleName, ruleType, null, null, curEffectiveLength, slots));
        }
        if (matchSluResults.isEmpty())
            res.addAll(noMatchSluResults);
        else
            res.addAll(matchSluResults);
        return res;

    }

    /**
     * description:若有多个均是##下的slot时，应合并在一起
     *
     * @return 后处理后的理解结果SLUResult
     * @Param: Gate匹配结果gateResults
     */
    public ArrayList<SLUResult> combineGateResult(ArrayList<SLUResult> gateResults) {
        ArrayList<SLUResult> gateResultsCom = new ArrayList<>();
        ArrayList<SLUResult> gateResultTmp = new ArrayList<>();//处理##情况
        for (SLUResult res : gateResults) {
            if (!res.ruleName.equals("##")) {
                gateResultsCom.add(res);
            } else {
                gateResultTmp.add(res);
            }
        }
        if (gateResultTmp.size() > 0) {
            Map<String, String> slotsCom = new HashMap<>();
            for (SLUResult res : gateResultTmp) {
                for (String key : res.slots.keySet()) {
                    slotsCom.put(key, res.slots.get(key));
                }
            }
            if (slotsCom.size() > 0) {
                gateResultsCom.add(new SLUResult(null, "##", "stateNode", null, null, slotsCom.size(), slotsCom));
            }
        }
        return gateResultsCom;
    }

    /**
     * description: 若筛选后还有多个，合成多个候选回复
     *
     * @return 冲突回复
     * @Param: 候选理解结果filterSet
     */
    private String checkVariable(Set<Annotation> filterSet) {

        String str = "";
        Map<String, Set<String>> varMap = new HashMap<>();

        for (Annotation i : filterSet) {
            String action = (String) i.getFeatures().get("command");
            FeatureMap oriFeatures = (FeatureMap) i.getFeatures().get("slots");
            for (Object key : oriFeatures.keySet()) {
                String str_userinput = (String) ((FeatureMap) oriFeatures.get(key)).get("matchedString");
                String str_standardName = (String) ((FeatureMap) oriFeatures.get(key)).get("抽取名");
                Set<String> stringList = varMap.get(str_userinput);
                if (stringList == null) {
                    stringList = new HashSet<>();
                    varMap.put(str_userinput, stringList);
                }
                stringList.add(str_standardName);
            }
        }

        for (String str_userinput : varMap.keySet()) {
            if (varMap.get(str_userinput).size() > 1) {
                str = str_userinput + "可能是";
                String strJoin = JoinStr(varMap.get(str_userinput), "、");
                str += strJoin;
                str += "，请问您要选择哪一个？";
                break;
            }
        }
        return str;
    }

    private String JoinStr(Collection<String> strlist, String sep) {
        String str = "";
        for (String string : strlist) {
            str += string;
            str += sep;
        }
        if (strlist.size() > 0) {
            str = str.substring(0, str.length() - sep.length());
        }
        return str;
    }

    private List<AbstractGazetteer> getAbstractGazetteer() {
        List<AbstractGazetteer> listGaz = new ArrayList<>();
        Iterator<ProcessingResource> prIter = analyser.getPRs().iterator();
        while (prIter.hasNext()) {
            ProcessingResource pr = (ProcessingResource) prIter.next();
            if (AbstractGazetteer.class.isAssignableFrom(pr.getClass())) {
                listGaz.add((AbstractGazetteer) pr);
            }
        }
        return listGaz;
    }

    public SerialAnalyserController getAnalyser() {
        return analyser;
    }

    public void setAnalyser(SerialAnalyserController analyser) {
        this.analyser = analyser;
    }
}


